﻿namespace Arms.Framework
{
    using System;
    using System.Globalization;

    public static class ConversionExtensions
    {
        #region Boolean
        /// <summary>
        /// Determines this object is a bool
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsBool(this object source)
        {
            bool boolValue;
            return bool.TryParse(source.ToNullableString(), out boolValue);
        }
        /// <summary>
        /// Tries to convert this nullable bool to bool
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultIfNull">if the nullable bool is null, returns this source</param>
        /// <returns></returns>
        public static bool ToBool(this bool? source, bool defaultIfNull)
        {
            bool boolValue;

            if (bool.TryParse(source.ToNullableString(), out boolValue))
            {
                return boolValue;
            }

            return defaultIfNull;
        }
        /// <summary>
        /// Tries to convert this object to a bool
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool ToBool(this object source)
        {
            bool boolValue;

            if (source == null)
            {
                return false;
            }
            else
            {
                if (!bool.TryParse(source.ToNullableString(), out boolValue))
                {
                    boolValue = (source.ToNullableString() == "1" || source.ToNullableInt() == 1);
                }
            }

            return boolValue;
        }
        /// <summary>
        /// Tries to convert this object to a nullable bool
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsucessful</returns>
        public static bool? ToNullableBool(this object source)
        {
            bool boolValue;

            if (bool.TryParse(source.ToNullableString(), out boolValue))
            {
                return boolValue;
            }
            else if (source.ToNullableString() == "1" || source.ToNullableInt() == 1)
            {
                return true;
            }
            else if (source.ToNullableString() == "0" || source.ToNullableInt() == 0)
            {
                return false;
            }
            else
            {
                return null;
            }
        }
        #endregion

        #region Byte
        /// <summary>
        /// Determines this object is a byte
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsByte(this object source)
        {
            byte byteValue;
            return byte.TryParse(source.ToNullableString(), out byteValue);
        }
        /// <summary>
        /// Tries to convert this object to a byte
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultValue">If conversion fails, returns this source. Default is zero</param>
        /// <returns></returns>
        public static byte ToByte(this object source, byte defaultValue = 0)
        {
            byte byteValue;

            if (byte.TryParse(source.ToNullableString(), out byteValue))
            {
                return byteValue;
            }

            return defaultValue;
        }
        /// <summary>
        /// Tries to convert this object to a nullable byte
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static byte? ToNullableByte(this object source)
        {
            byte byteValue;

            if (byte.TryParse(source.ToNullableString(), out byteValue))
            {
                return byteValue;
            }

            return null;
        }
        #endregion

        #region DateTime
        /// <summary>
        /// Determines this object is DateTime
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsDateTime(this object source)
        {
            DateTime datetimeValue;
            return DateTime.TryParse(source.ToNullableString(), out datetimeValue);
        }
        /// <summary>
        /// Tries to convert this object to DateTime
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(this object source)
        {
            DateTime datetimeValue;
            DateTime.TryParse(source.ToNullableString(), out datetimeValue);

            return datetimeValue;
        }
        /// <summary>
        /// Tries to convert this object to a nullable DateTime
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static DateTime? ToNullableDateTime(this object source)
        {
            DateTime datetimeValue;

            if (DateTime.TryParse(source.ToNullableString(), out datetimeValue))
            {
                return datetimeValue;
            }

            return null;
        }
        #endregion

        #region Decimal
        /// <summary>
        /// Determines this object is a decimal
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsDecimal(this object source)
        {
            decimal decimalValue;
            return decimal.TryParse(source.ToNullableString(), out decimalValue);
        }
        /// <summary>
        /// Tries to convert this object to a decimal
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static decimal ToDecimal(this object source)
        {
            decimal? newValue = source.ToNullableDecimal();

            if (newValue.HasValue)
            {
                return newValue.Value;
            }
            else
            {
                throw new Exception("Unable to parse decimal.");
            }
        }
        /// <summary>
        /// Tries to convert this object to a nullable decimal
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static decimal? ToNullableDecimal(this object source)
        {
            decimal decimalValue;

            if (decimal.TryParse(source.ToNullableString(), out decimalValue))
            {
                return decimalValue;
            }
            else if (decimal.TryParse(source.ToNullableString(), out decimalValue))
            {
                return decimalValue;
            }
            else
            {
                return null;
            }
        }
        #endregion

        #region Double
        /// <summary>
        /// Determines this object is a double
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsDouble(this object source)
        {
            double doubleValue;
            return double.TryParse(source.ToNullableString(), out doubleValue);
        }
        /// <summary>
        /// Tries to convert this object to a double
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultValue">If conversion fails, returns this source. Default is zero</param>
        /// <returns></returns>
        public static double ToDouble(this object source, double defaultValue = 0.0)
        {
            double? doubleValue = source.ToNullableDouble();

            if (doubleValue.HasValue)
            {
                return doubleValue.Value;
            }
            else
            {
                return defaultValue;
            }
        }
        /// <summary>
        /// Tries to convert this object to a nullable double
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static double? ToNullableDouble(this object source)
        {
            double doubleValue;

            if (double.TryParse(source.ToNullableString(), out doubleValue))
            {
                return doubleValue;
            }
            else if (double.TryParse(source.ToNullableString(), out doubleValue))
            {
                return doubleValue;
            }
            else
            {
                return null;
            }
        }
        #endregion

        #region Int
        /// <summary>
        /// Determines this object is an int
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsInt(this object source)
        {
            int intValue;
            return int.TryParse(source.ToNullableString(), out intValue);
        }
        /// <summary>
        /// Tries to convert this object to an int
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultValue">If the conversion fails, returns this source. Default is zero</param>
        /// <returns></returns>
        public static int ToInt(this object source, int defaultValue = 0)
        {
            int intValue;

            if (int.TryParse(source.ToNullableString(), out intValue))
            {
                return intValue;
            }

            return defaultValue;
        }
        /// <summary>
        /// Tries to convert this object to a nullable int
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static int? ToNullableInt(this object source)
        {
            int intValue;

            if (int.TryParse(source.ToNullableString(), out intValue))
            {
                return intValue;
            }

            return null;
        }
        #endregion

        #region Long

        /// <summary>
        /// Determines this object is a long
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsLong(this object source)
        {
            long longValue;
            return long.TryParse(source.ToNullableString(), out longValue);
        }
        /// <summary>
        /// Tries to convert this object to long
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultValue">If the conversion fails, returns this source. Default is zero</param>
        /// <returns></returns>
        public static long ToLong(this object source, int defaultValue = 0)
        {
            long longValue;

            if (long.TryParse(source.ToNullableString(), out longValue))
            {
                return longValue;
            }

            return defaultValue;
        }
        /// <summary>
        /// Tries to convert this object to a nullable long
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static long? ToNullableLong(this object source)
        {
            long longValue;

            if (long.TryParse(source.ToNullableString(), out longValue))
            {
                return longValue;
            }

            return null;
        }

        #endregion

        #region Short

        /// <summary>
        /// Determines this object is a short
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsShort(this object source)
        {
            short shortValue;
            return short.TryParse(source.ToNullableString(), out shortValue);
        }
        /// <summary>
        /// Tries to convert this object to a short
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultValue">If conversion fails, returns this source. Default is zero</param>
        /// <returns></returns>
        public static short ToShort(this object source, short defaultValue = 0)
        {
            short shortValue;

            if (short.TryParse(source.ToNullableString(), out shortValue))
            {
                return shortValue;
            }

            return defaultValue;
        }
        /// <summary>
        /// Tries to convert this object to a nullable short
        /// </summary>
        /// <param name="source"></param>
        /// <returns>null if unsuccessful</returns>
        public static short? ToNullableShort(this object source)
        {
            short shortValue;

            if (short.TryParse(source.ToNullableString(), out shortValue))
            {
                return shortValue;
            }

            return null;
        }

        #endregion

        #region String

        /// <summary>
        /// Converts this object to string representation with lower cases.
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string ToLowerString(this object source)
        {
            return source.ToNullableString().ToLower();
        }
        /// <summary>
        /// Converts the object to a string
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defaultNullValue">The source to return if object source is null. Default is string.Empty</param>
        /// <returns></returns>
        public static string ToNullableString(this object source, string defaultNullValue = "")
        {
            if (source != null)
            {
                if (!string.IsNullOrEmpty(source.ToString()))
                {
                    return source.ToString().Trim();
                }
            }

            return defaultNullValue;
        }

        #endregion
    }
}
